﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Non Maximum suppression
    /// The Non-Maximum Suppression (NMS) module will set all pixels in the current neighborhood window 
    /// that are lower than the maximum value in that window to zero (or black). 
    /// The module is similar to the Max Filter in that the maximum value for the specified window size
    /// (or current ROI area) is calculated. 
    /// The current pixel is then compared to this maximum value. 
    /// If lower it is set to black otherwise the value is unchanged.    /// </summary>
    public class NonMaximumSuppression : BaseImageFilter
    {
        protected SISThreshold m_SISThreshold = new SISThreshold();

        /// <summary>
        /// Initializes a new instance of the <see cref="NonMaximumSuppression"/> class.
        /// </summary>
        public NonMaximumSuppression()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="NonMaximumSuppression"/> class.
        /// </summary>
        /// <param name="_nonMaximumSuppression">The _non maximum suppression.</param>
        internal NonMaximumSuppression(NonMaximumSuppression _nonMaximumSuppression)
        {
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap input = (UnsafeBitmap)_bitmap.Clone();
            UnsafeBitmap output = _bitmap;

            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Width;

            int[] convolveX = { -1, 0, 1, -1, 0, 1, -1, 0, 1 };
            int[] convolveY = { -1, -1, -1, 0, 0, 0, 1, 1, 1 };
            int templateSize = 3;

            int width = _bitmap.Width;
            int height = _bitmap.Height;

            // first convolute image with x and y templates
            int[,] diffx = new int[width, height];
            int[,] diffy = new int[width, height];
            int[,] mag = new int[width, height];

            int valx, valy;

            for (int x = templateSize / 2; x < width - (templateSize / 2); x++)
            {
                for (int y = templateSize / 2; y < height - (templateSize / 2); y++)
                {
                    valx = 0;
                    valy = 0;
                    for (int x1 = 0; x1 < templateSize; x1++)
                    {
                        for (int y1 = 0; y1 < templateSize; y1++)
                        {
                            int pos = (y1 * templateSize + x1);
                            int imPosX = (x + (x1 - (templateSize / 2)));
                            int imPosY = (y + (y1 - (templateSize / 2)));

                            valx += ((input[imPosX, imPosY].ToArgb() & 0xff) * convolveX[pos]);
                            valy += ((input[imPosX, imPosY].ToArgb() & 0xff) * convolveY[pos]);
                        }
                    }
                    diffx[x, y] = valx;
                    diffy[x, y] = valy;
                    mag[x, y] = (int)(Math.Sqrt((valx * valx) + (valy * valy)));
                }
            }

            for (int x = 1; x < width - 1; x++)
            {
                //progress++;
                for (int y = 1; y < height - 1; y++)
                {
                    int dx, dy;

                    if (diffx[x, y] > 0) dx = 1;
                    else dx = -1;

                    if (diffy[x, y] > 0) dy = 1;
                    else dy = -1;

                    int a1, a2, b1, b2, A, B, point, val;
                    if (Math.Abs(diffx[x, y]) > Math.Abs(diffy[x, y]))
                    {
                        a1 = mag[x + dx, y];
                        a2 = mag[x + dx, y - dy];
                        b1 = mag[x - dx, y];
                        b2 = mag[x - dx, y + dy];
                        A = (Math.Abs(diffx[x, y]) - Math.Abs(diffy[x, y])) * a1 + Math.Abs(diffy[x, y]) * a2;
                        B = (Math.Abs(diffx[x, y]) - Math.Abs(diffy[x, y])) * b1 + Math.Abs(diffy[x, y]) * b2;
                        point = mag[x, y] * Math.Abs(diffx[x, y]);
                        if (point >= A && point > B)
                        {
                            val = Math.Abs(diffx[x, y]);
                            output[x, y] = Color.FromArgb((int)(val) << 16 | (int)(val) << 8 | (int)(val));
                        }
                        else
                        {
                            val = 0;
                            output[x, y] = Color.FromArgb((int)(val) << 16 | (int)(val) << 8 | (int)(val));
                        }
                    }
                    else
                    {
                        a1 = mag[x, y - dy];
                        a2 = mag[x + dx, y - dy];
                        b1 = mag[x, y + dy];
                        b2 = mag[x - dx, y + dy];
                        A = (Math.Abs(diffy[x, y]) - Math.Abs(diffx[x, y])) * a1 + Math.Abs(diffx[x, y]) * a2;
                        B = (Math.Abs(diffy[x, y]) - Math.Abs(diffx[x, y])) * b1 + Math.Abs(diffx[x, y]) * b2;
                        point = mag[x, y] * Math.Abs(diffy[x, y]);
                        if (point >= A && point > B)
                        {
                            val = Math.Abs(diffy[x, y]);
                            output[x, y] = Color.FromArgb((int)(val) << 16 | (int)(val) << 8 | (int)(val));
                        }
                        else
                        {
                            val = 0;
                            output[x, y] = Color.FromArgb((int)(val) << 16 | (int)(val) << 8 | (int)(val));
                        }
                    }
                }
            }
            m_SISThreshold.Execute(_bitmap);
            return _bitmap;
        }


        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new NonMaximumSuppression(this);
        }
    }
}
